package appStarshipPushups.client.form;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;

import appStarshipPushups.client.model.Connection;
import appStarshipPushups.client.model.ConnectionException;
import appStarshipPushups.common.Protocole;
import appStarshipPushups.common.Starship;

public class GamePanel extends JPanel implements KeyListener, ActionListener
{
	public static final long serialVersionUID = 1L;
	// Constantes des chemins des graphiques.
	private static final String BG_PATH = "resources/bg.png";
	private static final String PLAYER_PATH = "resources/player_";
	private static final String ENEMY_PATH = "resources/enemy_";
	private static final String IMG_EXT = ".png";
	
	// Messages d'erreur.
	private static final String GAME_ALREADY_STARTED = "Impossible de créer une partie lorsqu'une est déjà commencée.";
	private static final String CANT_SEND_MOVE_CMD = "Impossible d'envoyer le message de déplacement vers la direction : ";
	/**
	 * Cette constante représente la couleur des lasers.
	 */
	private static final Color LASER_COLOR = Color.yellow;
	
	/**
	 * Ce membre représente l'image de fond de l'espace de jeu.
	 */
	private Image bgImage = new ImageIcon(this.getClass().getResource(GamePanel.BG_PATH)).getImage();
	
	/**
	 * Ce membre représente toutes les étiquettes des vaisseaux.
	 */
	private JLabel[] lblStarships = null;
	/**
	 * Ce membre permet de connaître l'état du jeu.
	 */
	private boolean isGameStarted = false;
	/**
	 * Ce membre permet de savoir quelles flèches sont appuyées.
	 */
	private boolean[] keysOn = {false, false, false, false};
	/**
	 * Ce membre représente le Timer de déplacement du vaisseau.
	 */
	private Timer moveTimer = new Timer(50, this);
	/**
	 * Ce membre représente le numéro d'identification du vaisseau
	 * que le joueur contrôle.
	 */
	private int mainPlayerID = 0;
	/**
	 * Ce membre permet de savoir si la barre d'espacement est enfoncée.
	 */
	private boolean isSpacePressed = false;
	
	
	/**
	 * C'est le constructeur par défaut.
	 */
	public GamePanel()
	{
		super();
		this.init();
	}
	
	/**
	 * Cette fonction initialise le panneau sans démarrer la partie.
	 */
	private void init()
	{
		this.setLayout(null);
		this.setOpaque(false);
		this.setFocusable(true);
		
		this.setPreferredSize(new Dimension(Protocole.SCREEN_SIZE_WIDTH, Protocole.SCREEN_SIZE_HEIGHT));
		
		this.setBorder(BorderFactory.createLoweredBevelBorder());
	}
	
	/**
	 * Cette fonction permet d'obtenir la liste des étiquettes des vaisseaux.
	 * @return La liste des vaisseaux.
	 */
	public JLabel[] getLblStarships()
	{
		return this.lblStarships;
	}
	
	/**
	 * Cette fonction permet de modifier le numéro d'identification du
	 * joueur principal.
	 * @param aID Le numéro d'identification du joueur principale.
	 */
	public void setMainPlayerID(int aID)
	{
		this.mainPlayerID = aID;
	}
	
	/**
	 * Cette fonction démarre la partie.
	 */
	public void startGame()
	{
		if(!this.isGameStarted)
		{
			// On créé la liste d'ennemis.
			Protocole.getShips().clear();
			this.lblStarships = new JLabel[16];
			for(int i = 0; i < this.lblStarships.length; i++)
			{
				this.lblStarships[i] = new JLabel();
			}
			this.isGameStarted = true;
			// On ajoute le KeyListener.
			this.addKeyListener(this);
			this.moveTimer.setInitialDelay(0);
			this.moveTimer.start();
		}
		else
		{
			System.err.println(GamePanel.GAME_ALREADY_STARTED);
		}
	}
	
	/**
	 * Cette fonction synchronise l'étiquette et le vaisseau passés en 
	 * paramètre.
	 * @param aLbl L'étiquette à synchroniser.
	 * @param aStarship Le vaisseau à synchroniser.
	 */
	public void setLbl(JLabel aLbl, Starship aStarship)
	{
		if(aStarship.getID() == this.mainPlayerID)
		{
			aLbl.setIcon(new ImageIcon(this.getClass().getResource(GamePanel.PLAYER_PATH
					+ aStarship.getDirection() + GamePanel.IMG_EXT)));
		}
		else
		{
			aLbl.setIcon(new ImageIcon(this.getClass().getResource(GamePanel.ENEMY_PATH 
					+ aStarship.getDirection() + GamePanel.IMG_EXT)));
		}
		aLbl.setLocation(aStarship.getPosition());
		this.repaint();
	}
	
	/**
	 * Cette fonction termine la partie.
	 */
	public void endGame()
	{
		this.removeAll();
		for(int i = 0; i < this.getComponentCount(); i++)
		{
			if(this.getComponent(i) instanceof JLabel)
			{
				this.remove(this.getComponent(i));
			}
		}

		for(int i = 0; i < this.lblStarships.length; i++)
		{
			this.lblStarships[i] = null;
		}
		this.lblStarships = null;
		Protocole.getShips().clear();
		this.isGameStarted = false;
		this.removeKeyListener(this);
		this.moveTimer.stop();
		Protocole.getLasers().clear();
		Protocole.getShips().clear();
		this.repaint();
	}	
	
	/**
	 * Cette fonction permet de savoir si la partie est commencée.
	 * @return Retourne une valeur booléenne représentant l'état de la partie.
	 */
	public boolean getIsGameStarted()
	{
		return this.isGameStarted;
	}
	
	public void paintComponent(Graphics g)
	{		
		g.drawImage(this.bgImage, 0, 0, this);
				
		// On dessine les lasers.
		for(int i = 0; i < Protocole.getLasers().size(); i++)
		{
			g.setColor(GamePanel.LASER_COLOR);
			g.drawLine(Protocole.getLasers().get(i).getLaserEmplacement().getPosition().x,
					Protocole.getLasers().get(i).getLaserEmplacement().getPosition().y, 
					Protocole.getLasers().get(i).getLaserEmplacement().getSizePosition().x, 
					Protocole.getLasers().get(i).getLaserEmplacement().getSizePosition().y);
		}
		super.paintComponent(g);
	}
	
	/**
	 * Cette fonction rafraîchi le JLabel à afficher.
	 * @param id L'index à rafraîchir.
	 * @param newPosition La nouvelle position.
	 * @param newDirection La nouvelle direction.
	 */
	public void setPlayer(int id, Point newPosition, int newDirection)
	{
		if(this.isGameStarted)
		{
			if(id < this.lblStarships.length)
			{
				this.lblStarships[id].setBounds(newPosition.x,
						newPosition.y, Starship.SHIP_WIDTH,
						Starship.SHIP_HEIGHT);
				if(id == this.mainPlayerID)
				{
					this.lblStarships[id].setIcon(new ImageIcon(this.getClass().getResource(GamePanel.PLAYER_PATH
							+ Integer.toString(newDirection)
							+ GamePanel.IMG_EXT)));
				}
				else
				{
					this.lblStarships[id].setIcon(new ImageIcon(this.getClass().getResource(GamePanel.ENEMY_PATH
						+ Integer.toString(newDirection)
						+ GamePanel.IMG_EXT)));
				}
				if(this.lblStarships[id].getParent() != this)
				{
					this.add(this.lblStarships[id]);
				}
			}
		}
	}
	
	// Enlever un laser spécifique de la surface de dessin.
	public void removeLaser(int aLaserId)
	{
		Protocole.getLasers().remove(aLaserId);
	}
		
	 /**
     * Invoked when a key has been typed.
     * See the class description for {@link KeyEvent} for a definition of 
     * a key typed event.
     */
    public void keyTyped(KeyEvent e)
    {
    }

    /**
     * Invoked when a key has been pressed. 
     * See the class description for {@link KeyEvent} for a definition of 
     * a key pressed event.
     */
    public void keyPressed(KeyEvent e)
    {
    	int keyCode = e.getKeyCode();
    	switch(keyCode)
    	{
    	case KeyEvent.VK_UP:
    		if(this.isSpacePressed)
    		{
    			this.keysOn[Starship.DIRECTION_UP] = false;
    		}
    		else
    		{
    			this.keysOn[Starship.DIRECTION_UP] = true;
    		}
    		break;
    	case KeyEvent.VK_DOWN:
    		if(this.isSpacePressed)
    		{
    			this.keysOn[Starship.DIRECTION_DOWN] = false;
    		}
    		else
    		{
    			this.keysOn[Starship.DIRECTION_DOWN] = true;
    		}
    		break;
    	case KeyEvent.VK_LEFT:
    		if(this.isSpacePressed)
    		{
    			this.keysOn[Starship.DIRECTION_LEFT] = false;
    		}
    		else
    		{
    			this.keysOn[Starship.DIRECTION_LEFT] = true;
    		}
    		break;
    	case KeyEvent.VK_RIGHT:
    		if(this.isSpacePressed)
    		{
    			this.keysOn[Starship.DIRECTION_RIGHT] = false;
    		}
    		else
    		{
    			this.keysOn[Starship.DIRECTION_RIGHT] = true;
    		}
    		break;
    	case KeyEvent.VK_SPACE:
    		// Envoyer un message pour indiquer d'activer le laser.
    		try
    		{
    			boolean keyPressed = false;
    			for(int i = 0; !keyPressed && i < this.keysOn.length; i++)
    			{
    				if(this.keysOn[i])
    				{
    					keyPressed = true;
    				}
    			}
    			if(!keyPressed && !this.isSpacePressed)
    			{
    				Connection.getInstance().sendMsg(Protocole.FUNCTION_SHOOT_ON);
    				this.isSpacePressed = true;
    			}
    		}
    		catch(ConnectionException ce)
    		{
    			System.err.println(ce.getMessage());
    		}
    		break;
    	}
    }

    /**
     * Invoked when a key has been released.
     * See the class description for {@link KeyEvent} for a definition of 
     * a key released event.
     */
    public void keyReleased(KeyEvent e)
    {
    	int keyCode = e.getKeyCode();
    	switch(keyCode)
    	{
    	case KeyEvent.VK_UP:
    		this.keysOn[Starship.DIRECTION_UP] = false;
    		break;
    	case KeyEvent.VK_DOWN:
    		this.keysOn[Starship.DIRECTION_DOWN] = false;
    		break;
    	case KeyEvent.VK_LEFT:
    		this.keysOn[Starship.DIRECTION_LEFT] = false;
    		break;
    	case KeyEvent.VK_RIGHT:
    		this.keysOn[Starship.DIRECTION_RIGHT] = false;
    		break;
    	case KeyEvent.VK_SPACE:
    		// Envoyer un message pour indiquer d'activer le laser.
			try
			{
				Connection.getInstance().sendMsg(Protocole.FUNCTION_SHOOT_OFF);
			}
			catch(ConnectionException ce)
			{
				System.err.println(ce.getMessage());
			}
			this.isSpacePressed = false;
	    	break;
    	}
    }
    
    /**
     * Appelée lorsqu'une action se produit.
     */
    public void actionPerformed(ActionEvent e)
    {
    	if(e.getSource() instanceof Timer)
    	{
    		for(int i = 0; i < this.keysOn.length; i++)
    		{
    			if(this.keysOn[i])
    			{
    				switch(i)
    				{
    		    	case Starship.DIRECTION_UP:
    		    		try
    		    		{
    		    			Connection.getInstance().sendMoveMessage(Protocole.UP);
    		    		}
    		    		catch(ConnectionException ce)
    		    		{
    		    			System.err.println(GamePanel.CANT_SEND_MOVE_CMD + Protocole.UP);
    		    		}
    		    		break;
    		    	case Starship.DIRECTION_DOWN:
    		    		try
    		    		{
    		    			Connection.getInstance().sendMoveMessage(Protocole.DOWN);
    		    		}
    		    		catch(ConnectionException ce)
    		    		{
    		    			System.err.println(GamePanel.CANT_SEND_MOVE_CMD + Protocole.DOWN);
    		    		}
    		    		break;
    		    	case Starship.DIRECTION_LEFT:
    		    		try
    		    		{
    		    			Connection.getInstance().sendMoveMessage(Protocole.LEFT);
    		    		}
    		    		catch(ConnectionException ce)
    		    		{
    		    			System.err.println(GamePanel.CANT_SEND_MOVE_CMD + Protocole.LEFT);
    		    		}
    		    		break;
    		    	case Starship.DIRECTION_RIGHT:
    		    		try
    		    		{
    		    			Connection.getInstance().sendMoveMessage(Protocole.RIGHT);
    		    		}
    		    		catch(ConnectionException ce)
    		    		{
    		    			System.err.println(GamePanel.CANT_SEND_MOVE_CMD + Protocole.RIGHT);
    		    		}
    		    		break;
    				}
    			}
    		}
    	}
    }

}
